// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../../include/fxge/fx_ge.h"
#if _FX_OS_ == _FX_WIN32_DESKTOP_ ||  _FX_OS_ == _FX_WIN64_DESKTOP_
#include <windows.h>
#include "../../../include/fxge/fx_ge_win32.h"
#include "win32_int.h"
CFX_ByteString CFX_WindowsDIB::GetBitmapInfo(const CFX_DIBitmap* pBitmap)
{
    CFX_ByteString result;
    int len = sizeof (BITMAPINFOHEADER);
    if (pBitmap->GetBPP() == 1 || pBitmap->GetBPP() == 8) {
        len += sizeof (DWORD) * (int)(1 << pBitmap->GetBPP());
    }
    BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)result.GetBuffer(len);
    FXSYS_memset(pbmih, 0, sizeof (BITMAPINFOHEADER));
    pbmih->biSize = sizeof(BITMAPINFOHEADER);
    pbmih->biBitCount = pBitmap->GetBPP();
    pbmih->biCompression = BI_RGB;
    pbmih->biHeight = -(int)pBitmap->GetHeight();
    pbmih->biPlanes = 1;
    pbmih->biWidth = pBitmap->GetWidth();
    if (pBitmap->GetBPP() == 8) {
        FX_DWORD* pPalette = (FX_DWORD*)(pbmih + 1);
        if (pBitmap->GetPalette() == NULL) {
            for (int i = 0; i < 256; i ++) {
                pPalette[i] = i * 0x010101;
            }
        } else {
            for (int i = 0; i < 256; i ++) {
                pPalette[i] = pBitmap->GetPalette()[i];
            }
        }
    }
    if (pBitmap->GetBPP() == 1) {
        FX_DWORD* pPalette = (FX_DWORD*)(pbmih + 1);
        if (pBitmap->GetPalette() == NULL) {
            pPalette[0] = 0;
            pPalette[1] = 0xffffff;
        } else {
            pPalette[0] = pBitmap->GetPalette()[0];
            pPalette[1] = pBitmap->GetPalette()[1];
        }
    }
    result.ReleaseBuffer(len);
    return result;
}
CFX_DIBitmap* _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi, LPVOID pData, FX_BOOL bAlpha)
{
    int width = pbmi->bmiHeader.biWidth;
    int height = pbmi->bmiHeader.biHeight;
    BOOL bBottomUp = TRUE;
    if (height < 0) {
        height = -height;
        bBottomUp = FALSE;
    }
    int pitch = (width * pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
    CFX_DIBitmap* pBitmap = new CFX_DIBitmap;
    FXDIB_Format format = bAlpha ? (FXDIB_Format)(pbmi->bmiHeader.biBitCount + 0x200) : (FXDIB_Format)pbmi->bmiHeader.biBitCount;
    FX_BOOL ret = pBitmap->Create(width, height, format);
    if (!ret) {
        delete pBitmap;
        return NULL;
    }
    FXSYS_memcpy(pBitmap->GetBuffer(), pData, pitch * height);
    if (bBottomUp) {
        uint8_t* temp_buf = FX_Alloc(uint8_t, pitch);
        int top = 0, bottom = height - 1;
        while (top < bottom) {
            FXSYS_memcpy(temp_buf, pBitmap->GetBuffer() + top * pitch, pitch);
            FXSYS_memcpy(pBitmap->GetBuffer() + top * pitch, pBitmap->GetBuffer() + bottom * pitch, pitch);
            FXSYS_memcpy(pBitmap->GetBuffer() + bottom * pitch, temp_buf, pitch);
            top ++;
            bottom --;
        }
        FX_Free(temp_buf);
        temp_buf = NULL;
    }
    if (pbmi->bmiHeader.biBitCount == 1) {
        for (int i = 0; i < 2; i ++) {
            pBitmap->SetPaletteEntry(i, ((FX_DWORD*)pbmi->bmiColors)[i] | 0xff000000);
        }
    } else if (pbmi->bmiHeader.biBitCount == 8) {
        for (int i = 0; i < 256; i ++) {
            pBitmap->SetPaletteEntry(i, ((FX_DWORD*)pbmi->bmiColors)[i] | 0xff000000);
        }
    }
    return pBitmap;
}
CFX_DIBitmap* CFX_WindowsDIB::LoadFromBuf(BITMAPINFO* pbmi, LPVOID pData)
{
    return _FX_WindowsDIB_LoadFromBuf(pbmi, pData, FALSE);
}
HBITMAP	CFX_WindowsDIB::GetDDBitmap(const CFX_DIBitmap* pBitmap, HDC hDC)
{
    CFX_ByteString info = GetBitmapInfo(pBitmap);
    HBITMAP hBitmap = NULL;
    hBitmap = CreateDIBitmap(hDC, (BITMAPINFOHEADER*)info.c_str(), CBM_INIT,
        pBitmap->GetBuffer(), (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
    return hBitmap;
}
void GetBitmapSize(HBITMAP hBitmap, int& w, int& h)
{
    BITMAP bmp;
    GetObject(hBitmap, sizeof bmp, &bmp);
    w = bmp.bmWidth;
    h = bmp.bmHeight;
}
CFX_DIBitmap* CFX_WindowsDIB::LoadFromFile(const FX_WCHAR* filename)
{
    CWin32Platform* pPlatform = (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
    if (pPlatform->m_GdiplusExt.IsAvailable()) {
        WINDIB_Open_Args_ args;
        args.flags = WINDIB_OPEN_PATHNAME;
        args.path_name = filename;
        return pPlatform->m_GdiplusExt.LoadDIBitmap(args);
    }
    HBITMAP hBitmap = (HBITMAP)LoadImageW(NULL, (wchar_t*)filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    if (hBitmap == NULL) {
        return NULL;
    }
    HDC hDC = CreateCompatibleDC(NULL);
    int width, height;
    GetBitmapSize(hBitmap, width, height);
    CFX_DIBitmap* pDIBitmap = new CFX_DIBitmap;
    if (!pDIBitmap->Create(width, height, FXDIB_Rgb)) {
        delete pDIBitmap;
        DeleteDC(hDC);
        return NULL;
    }
    CFX_ByteString info = GetBitmapInfo(pDIBitmap);
    int ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(), (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
    if (!ret) {
        delete pDIBitmap;
        pDIBitmap = NULL;
    }
    DeleteDC(hDC);
    return pDIBitmap;
}
CFX_DIBitmap* CFX_WindowsDIB::LoadDIBitmap(WINDIB_Open_Args_ args)
{
    CWin32Platform* pPlatform = (CWin32Platform*)CFX_GEModule::Get()->GetPlatformData();
    if (pPlatform->m_GdiplusExt.IsAvailable()) {
        return pPlatform->m_GdiplusExt.LoadDIBitmap(args);
    } else if (args.flags == WINDIB_OPEN_MEMORY) {
        return NULL;
    }
    HBITMAP hBitmap = (HBITMAP)LoadImageW(NULL, (wchar_t*)args.path_name, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    if (hBitmap == NULL) {
        return NULL;
    }
    HDC hDC = CreateCompatibleDC(NULL);
    int width, height;
    GetBitmapSize(hBitmap, width, height);
    CFX_DIBitmap* pDIBitmap = new CFX_DIBitmap;
    if (!pDIBitmap->Create(width, height, FXDIB_Rgb)) {
        delete pDIBitmap;
        DeleteDC(hDC);
        return NULL;
    }
    CFX_ByteString info = GetBitmapInfo(pDIBitmap);
    int ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(), (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
    if (!ret) {
        delete pDIBitmap;
        pDIBitmap = NULL;
    }
    DeleteDC(hDC);
    return pDIBitmap;
}
CFX_DIBitmap* CFX_WindowsDIB::LoadFromDDB(HDC hDC, HBITMAP hBitmap, FX_DWORD* pPalette, FX_DWORD palsize)
{
    FX_BOOL bCreatedDC = hDC == NULL;
    if (hDC == NULL) {
        hDC = CreateCompatibleDC(NULL);
    }
    BITMAPINFOHEADER bmih;
    FXSYS_memset(&bmih, 0, sizeof bmih);
    bmih.biSize = sizeof bmih;
    GetDIBits(hDC, hBitmap, 0, 0, NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS);
    int width = bmih.biWidth;
    int height = abs(bmih.biHeight);
    bmih.biHeight = -height;
    bmih.biCompression = BI_RGB;
    CFX_DIBitmap* pDIBitmap = new CFX_DIBitmap;
    int ret = 0;
    if (bmih.biBitCount == 1 || bmih.biBitCount == 8) {
        int size = sizeof (BITMAPINFOHEADER) + 8;
        if (bmih.biBitCount == 8) {
            size += sizeof (FX_DWORD) * 254;
        }
        BITMAPINFO* pbmih = (BITMAPINFO*)FX_Alloc(uint8_t, size);
        pbmih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        pbmih->bmiHeader.biBitCount = bmih.biBitCount;
        pbmih->bmiHeader.biCompression = BI_RGB;
        pbmih->bmiHeader.biHeight = -height;
        pbmih->bmiHeader.biPlanes = 1;
        pbmih->bmiHeader.biWidth = bmih.biWidth;
        if (!pDIBitmap->Create(bmih.biWidth, height, bmih.biBitCount == 1 ? FXDIB_1bppRgb : FXDIB_8bppRgb)) {
            delete pDIBitmap;
            FX_Free(pbmih);
            if (bCreatedDC) {
                DeleteDC(hDC);
            }
            return NULL;
        }
        ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(), (BITMAPINFO*)pbmih, DIB_RGB_COLORS);
        FX_Free(pbmih);
        pbmih = NULL;
        pDIBitmap->CopyPalette(pPalette, palsize);
    } else {
        if (bmih.biBitCount <= 24) {
            bmih.biBitCount = 24;
        } else {
            bmih.biBitCount = 32;
        }
        if (!pDIBitmap->Create(bmih.biWidth, height, bmih.biBitCount == 24 ? FXDIB_Rgb : FXDIB_Rgb32)) {
            delete pDIBitmap;
            if (bCreatedDC) {
                DeleteDC(hDC);
            }
            return NULL;
        }
        ret = GetDIBits(hDC, hBitmap, 0, height, pDIBitmap->GetBuffer(), (BITMAPINFO*)&bmih, DIB_RGB_COLORS);
        if (ret != 0 && bmih.biBitCount == 32) {
            int pitch = pDIBitmap->GetPitch();
            for (int row = 0; row < height; row ++) {
                uint8_t* dest_scan = (uint8_t*)(pDIBitmap->GetBuffer() + row * pitch);
                for (int col = 0; col < width; col++) {
                    dest_scan[3] = 255;
                    dest_scan += 4;
                }
            }
        }
    }
    if (ret == 0) {
        delete pDIBitmap;
        pDIBitmap = NULL;
    }
    if (bCreatedDC) {
        DeleteDC(hDC);
    }
    return pDIBitmap;
}
CFX_WindowsDIB::CFX_WindowsDIB(HDC hDC, int width, int height)
{
    Create(width, height, FXDIB_Rgb, (uint8_t*)1);
    BITMAPINFOHEADER bmih;
    FXSYS_memset(&bmih, 0, sizeof bmih);
    bmih.biSize = sizeof bmih;
    bmih.biBitCount = 24;
    bmih.biHeight = -height;
    bmih.biPlanes = 1;
    bmih.biWidth = width;
    m_hBitmap = CreateDIBSection(hDC, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, (LPVOID*)&m_pBuffer, NULL, 0);
    m_hMemDC = CreateCompatibleDC(hDC);
    m_hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hBitmap);
}
CFX_WindowsDIB::~CFX_WindowsDIB()
{
    SelectObject(m_hMemDC, m_hOldBitmap);
    DeleteDC(m_hMemDC);
    DeleteObject(m_hBitmap);
}
void CFX_WindowsDIB::LoadFromDevice(HDC hDC, int left, int top)
{
    ::BitBlt(m_hMemDC, 0, 0, m_Width, m_Height, hDC, left, top, SRCCOPY);
}
void CFX_WindowsDIB::SetToDevice(HDC hDC, int left, int top)
{
    ::BitBlt(hDC, left, top, m_Width, m_Height, m_hMemDC, 0, 0, SRCCOPY);
}
#endif
